package framework.storage;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import framework.config.S3Config;
import framework.io.FullBufferOutputStream;
import lombok.Getter;
import org.apache.commons.io.IOUtils;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * 使用本地存储的文件存储实现
 */
public class FileStorageS3v1 implements FileStorage {
    @Getter
    private S3Config config;
    @Getter
    private AmazonS3 s3;

    public FileStorageS3v1(S3Config config) {
        this.config = config;
        this.s3 = s3init();
    }

    @Override
    public String[] compressionPathStarts() {
        return getConfig().getCompressPathStarts();
    }

    /**
     * 获得s3对象
     *
     * @return s3
     */
    protected AmazonS3 s3init() {
        ClientConfiguration config = new ClientConfiguration();
        AwsClientBuilder.EndpointConfiguration endpointConfig = new AwsClientBuilder.EndpointConfiguration(getConfig().getEndPoint(), this.config.getRegion());
        AWSCredentials awsCredentials = new BasicAWSCredentials(this.config.getAccessKey(), this.config.getSecretKey());
        AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
        AmazonS3 s3 = AmazonS3Client.builder()
                .withEndpointConfiguration(endpointConfig)
                .withClientConfiguration(config)
                .withCredentials(awsCredentialsProvider)
                .withChunkedEncodingDisabled(!Objects.equals(true, this.config.getChunkedEncoding()))
                .withPathStyleAccessEnabled(this.config.getPathStyle())
                .build();
        return s3;
    }

    @Override
    public long save(InputStream inputStream, String filePath, boolean compression) throws IOException {
        if (compression) {
            filePath += ".gz";
        }

        Integer partSize = getConfig().getPartSize();
        String bucket = getConfig().getBucket();

        ObjectMetadata objectMetadata = new ObjectMetadata();
        // l2 = zip
        // l1 = buffer
        // l0 = s3
        long sl = 0L;
        try (FileStorageS3OutputStream os0 = new FileStorageS3OutputStream(getS3(), bucket, filePath, objectMetadata, partSize)) {
            try (OutputStream os1 = new FullBufferOutputStream(os0, partSize)) {
                if (compression) {
                    try (OutputStream os2 = new GZIPOutputStream(os1)) {
                        sl = IOUtils.copyLarge(inputStream, os2);
                        os2.flush();
                    }
                } else {
                    sl = IOUtils.copyLarge(inputStream, os1);
                }
                os1.flush();
            }
            os0.flush();
            os0.setWriteCompleted(true);
            return sl;
        }
    }

    @Override
    public InputStream getStream(String filePath, boolean compression) throws IOException {
        if (compression) {
            filePath += ".gz";
        }
        S3ObjectInputStream objectContent;
        try {
            S3Object object = getS3().getObject(getConfig().getBucket(), filePath);
            objectContent = object.getObjectContent();
        } catch (AmazonS3Exception exception) {
            if (exception.getStatusCode() == 404) {
                throw new FileNotFoundException("File not found (" + exception.getMessage() + ")");
            }
            throw new IOException("File not found", exception);
        }
        return compression ? new GZIPInputStream(objectContent) : objectContent;
    }

    @Override
    public void delete(String filePath, boolean compression) throws IOException {
        if (compression) {
            filePath += ".gz";
        }
        getS3().deleteObject(getConfig().getBucket(), filePath);
    }

    @Override
    public long length(String filePath, boolean compression) throws IOException {
        if (compression) {
            filePath += ".gz";
        }
        try {
            ObjectMetadata metadata = getS3().getObjectMetadata(getConfig().getBucket(), filePath);
            return metadata.getContentLength();
        } catch (AmazonS3Exception exception) {
            if (exception.getStatusCode() == 404) {
                return -1L;
            }
            throw new IOException("File not found", exception);
        }
    }

}
